﻿using Ini.Net;
using Stepon.FaceRecognization;
using Stepon.FaceRecognization.Common;
using Stepon.FaceRecognization.Detection;
using Stepon.FaceRecognization.Extensions;
using Stepon.FaceRecognization.Recognization;
using Stepon.FaceRecognization.Tracking;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Threading;

namespace FaceIdentifier
{
    public class FaceApiImpl : FaceApi
    {
        private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(FaceApiImpl));
        private const string Sn = "SN.ini";
        public const string Repository = "人脸库";
        private FaceTracking _traking;
        private FaceDetection _detection;
        private FaceRecognize _recognize;
        private FaceProcessor _processor;

        private readonly Dictionary<string, byte[]> _cache = new Dictionary<string, byte[]>();
        private readonly ReaderWriterLockSlim _cacheLock = new ReaderWriterLockSlim();
        private readonly Dictionary<string,PrivilegeInfo> _file = new Dictionary<string,PrivilegeInfo>();

        void FaceApi.Init()
        {
            try
            {
                if(_traking != null || _detection != null || _recognize != null | _processor != null)
                {
                    return;
                }

                var iniFile = new IniFile(Sn);
                if (!File.Exists(Sn))
                {
                    iniFile.WriteString("注册码", "AppId", "pdhEENp6Ph69tmrMaF6tUPHT3piYN8r1jAYa1Mi4VA5");
                    iniFile.WriteString("注册码", "FtKey", "BVAXc57zmq69RxHRvowrDpfMxjEYSV6yZZ7nygH99Rwt");
                    iniFile.WriteString("注册码", "FdKey", "BVAXc57zmq69RxHRvowrDpfV88VkEh9UXBEoH3kG266K");
                    iniFile.WriteString("注册码", "FrKey", "BVAXc57zmq69RxHRvowrDpfymjYSGh7MGBa9Kn8NiW54");
                }

                var appId = iniFile.ReadString("注册码", "AppId");
                var ftKey = iniFile.ReadString("注册码", "FtKey");
                var fdKey = iniFile.ReadString("注册码", "FdKey");
                var frKey = iniFile.ReadString("注册码", "FrKey");

                _traking = LocatorFactory.GetTrackingLocator(appId, ftKey) as FaceTracking;
                _detection = LocatorFactory.GetDetectionLocator(appId, fdKey) as FaceDetection;
                _recognize = new FaceRecognize(appId, frKey);
                _processor = new FaceProcessor(_traking, _recognize);

                Log.Info("初始化人脸识别动态库成功");
            }
            catch (Exception e)
            {
                Log.Error("初始化人脸识别动态库失败", e);
            }
        }


        void FaceApi.Add(string key, Bitmap bitmap)
        {
            AddBitmap(key, bitmap);
        }

        string FaceApi.Match(Bitmap bitmap)
        {
            if (bitmap == null) { return null; }
            try
            {
                var features = _processor.LocateExtract(bitmap);
                if (features == null || features.Length == 0) return null;
                string result = null;
                foreach (var item in features)
                {
                    if (item.Rect.ToRectangle().Width >= 100)
                    {
                        result = Match(item);
                    }
                    item.Dispose();
                }
                return result;
            }
            catch (Exception e)
            {
                Log.Error("人脸识别异常", e);
            }
            return "";
        }

        private string Match(Feature feature)
        {
            if (feature == null) { return null; }

            try
            {
                if (_cacheLock.TryEnterReadLock(1))
                {
                    foreach (var single in _cache)
                    {
                        var sim = _processor.Match(feature.FeatureData, single.Value); //此方法默认保留采集到的特征（非托管内存），并自动释放被比较（特征库）的特征数据，所以无需担心内存泄露
                        if (sim > 0.7)
                        {
                            return single.Key;
                        }
                    }
                }
            }
            finally
            {
                _cacheLock.ExitReadLock();
            }
            return "";
        }

        void FaceApi.Remove(string key)
        {
            try
            {
                _cacheLock.EnterReadLock();
                if (_cache.ContainsKey(key))
                {
                    _cache.Remove(key);
                }
            }
            finally
            {
                _cacheLock.ExitReadLock();
            }
            _file.Clear();
        }

        private void AddBitmap(string key, Bitmap bitmap)
        {
            FaceModel faceModel = ToFaceModel(bitmap);
            AddToCache(key, faceModel.Data);
        }

        /**
         * 转换人脸图片为人脸模板
         **/
        private FaceModel ToFaceModel(Bitmap bitmap)
        {
            _file.Clear();
            LocateResult locate;
            var code = _detection.Detect(bitmap, out locate);
            if (code == ErrorCode.Ok && locate.HasFace && locate.FaceCount == 1)
            {
                using (var feature = _recognize.ExtractFeature(locate.OffInput, locate.Faces[0], locate.FacesOrient[0]))
                {
                    return feature.FeatureData;
                }
            }
            locate.Dispose();
            return null;
        }

        /**
         * 加载人脸模板到1:N高速对比库
         **/
        private void AddToCache(string key, byte[] faceModel)
        {
            if (faceModel == null)
            {
                return;
            }
            try
            {
                _cacheLock.EnterWriteLock();
                if (_cache.ContainsKey(key))
                {
                    _cache.Remove(key);
                }
                _cache.Add(key, faceModel);
                Log.InfoFormat("添加人脸到对比库:{0}", key);
            }
            finally
            {
                _cacheLock.ExitWriteLock();
            }
        }

        PrivilegeInfo FaceApi.GetInfo(string key)
        {
            if(_file.Count == 0)
            {
                var files = Directory.GetFiles(Repository).Where(w => w.EndsWith("jpg"));
                foreach (var file in files)
                {
                    try
                    {
                        FileInfo fi = new FileInfo(file);
                        var fileNames = fi.Name.Replace(fi.Extension, "").Split('_');

                        PrivilegeInfo info = new PrivilegeInfo();
                        info.personSN = fileNames[0];
                        info.personName = fileNames[1];

                        _file.Add(info.personSN, info);
                    }
                    catch (Exception e)
                    {
                        Log.Error("添加人脸到对比库异常", e);
                    }
                }
            }
            PrivilegeInfo pi;
            _file.TryGetValue(key,out pi);
            return pi;
        }

        void FaceApi.ReloadFace()
        {
            Log.InfoFormat("准备加载本地人脸库:{0}", Repository);
            if (!Directory.Exists(Repository))
            {
                Log.InfoFormat("创建本地对比库:{0}", Repository);
                Directory.CreateDirectory(Repository);
                return;
            }


            try
            {
                _cacheLock.EnterWriteLock();
                _cache.Clear();
                Log.Info("清理1:N缓存区所有数据");
            }
            finally
            {
                _cacheLock.ExitWriteLock();
            }

            Log.InfoFormat("加载本地人脸对比库:{0}", Repository);
            var files = Directory.GetFiles(Repository).Where(w => w.EndsWith("jpg"));
            foreach (var file in files)
            {
                try
                {
                    var fi = new FileInfo(file);
                    var name = fi.Name.Replace(fi.Extension, ".data");
                    var data = Path.Combine(Repository, name);
                    if (!File.Exists(data))
                    {
                        using (Bitmap bitmap = new Bitmap(file))
                        {
                            try
                            {
                                var faceModel = ToFaceModel(bitmap);
                                if (faceModel != null)
                                {
                                    File.WriteAllBytes(data, faceModel);
                                }
                            }
                            catch(Exception ex)
                            {
                                Log.ErrorFormat("生成data文件异常：" + data, ex);
                                continue;
                            }
                        }
                    }
                    var key = name.Split('_')[0];
                    var faceData = File.ReadAllBytes(data);
                    AddToCache(key, faceData);
                }
                catch (Exception e)
                {
                    Log.ErrorFormat("添加人脸到对比库异常:" + file, e);
                }
            }
        }

        int FaceApi.FaceCount()
        {
            return _cache.Count;
        }

        public void UnInit()
        {
            _traking.Dispose();
            _recognize.Dispose();
            _processor.Dispose();
            _cacheLock.Dispose();
        }
    }
}
